/*
 * Copyright 2015 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.buffer;

import io.netty.util.internal.PlatformDependent;

import java.nio.ByteOrder;

import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;

/**
 * Special {@link SwappedByteBuf} for {@link ByteBuf}s that is using unsafe.
 */
abstract class AbstractUnsafeSwappedByteBuf extends SwappedByteBuf {

  private final boolean nativeByteOrder;
  private final AbstractByteBuf wrapped;

  AbstractUnsafeSwappedByteBuf(AbstractByteBuf buf) {
    super(buf);
    assert PlatformDependent.isUnaligned();
    wrapped = buf;
    nativeByteOrder = BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN);
  }

  @Override
  public final long getLong(int index) {
    wrapped.checkIndex(index, 8);
    long v = _getLong(wrapped, index);
    return nativeByteOrder ? v : Long.reverseBytes(v);
  }

  @Override
  public final float getFloat(int index) {
    return Float.intBitsToFloat(getInt(index));
  }

  @Override
  public final double getDouble(int index) {
    return Double.longBitsToDouble(getLong(index));
  }

  @Override
  public final char getChar(int index) {
    return (char) getShort(index);
  }

  @Override
  public final long getUnsignedInt(int index) {
    return getInt(index) & 0xFFFFFFFFL;
  }

  @Override
  public final int getInt(int index) {
    wrapped.checkIndex(index, 4);
    int v = _getInt(wrapped, index);
    return nativeByteOrder ? v : Integer.reverseBytes(v);
  }

  @Override
  public final int getUnsignedShort(int index) {
    return getShort(index) & 0xFFFF;
  }

  @Override
  public final short getShort(int index) {
    wrapped.checkIndex(index, 2);
    short v = _getShort(wrapped, index);
    return nativeByteOrder ? v : Short.reverseBytes(v);
  }

  @Override
  public final ByteBuf setShort(int index, int value) {
    wrapped.checkIndex(index, 2);
    _setShort(wrapped, index, nativeByteOrder ? (short) value : Short.reverseBytes((short) value));
    return this;
  }

  @Override
  public final ByteBuf setInt(int index, int value) {
    wrapped.checkIndex(index, 4);
    _setInt(wrapped, index, nativeByteOrder ? value : Integer.reverseBytes(value));
    return this;
  }

  @Override
  public final ByteBuf setLong(int index, long value) {
    wrapped.checkIndex(index, 8);
    _setLong(wrapped, index, nativeByteOrder ? value : Long.reverseBytes(value));
    return this;
  }

  @Override
  public final ByteBuf setChar(int index, int value) {
    setShort(index, value);
    return this;
  }

  @Override
  public final ByteBuf setFloat(int index, float value) {
    setInt(index, Float.floatToRawIntBits(value));
    return this;
  }

  @Override
  public final ByteBuf setDouble(int index, double value) {
    setLong(index, Double.doubleToRawLongBits(value));
    return this;
  }

  @Override
  public final ByteBuf writeShort(int value) {
    wrapped.ensureWritable0(2);
    _setShort(wrapped, wrapped.writerIndex,
        nativeByteOrder ? (short) value : Short.reverseBytes((short) value));
    wrapped.writerIndex += 2;
    return this;
  }

  @Override
  public final ByteBuf writeInt(int value) {
    wrapped.ensureWritable0(4);
    _setInt(wrapped, wrapped.writerIndex, nativeByteOrder ? value : Integer.reverseBytes(value));
    wrapped.writerIndex += 4;
    return this;
  }

  @Override
  public final ByteBuf writeLong(long value) {
    wrapped.ensureWritable0(8);
    _setLong(wrapped, wrapped.writerIndex, nativeByteOrder ? value : Long.reverseBytes(value));
    wrapped.writerIndex += 8;
    return this;
  }

  @Override
  public final ByteBuf writeChar(int value) {
    writeShort(value);
    return this;
  }

  @Override
  public final ByteBuf writeFloat(float value) {
    writeInt(Float.floatToRawIntBits(value));
    return this;
  }

  @Override
  public final ByteBuf writeDouble(double value) {
    writeLong(Double.doubleToRawLongBits(value));
    return this;
  }

  protected abstract short _getShort(AbstractByteBuf wrapped, int index);

  protected abstract int _getInt(AbstractByteBuf wrapped, int index);

  protected abstract long _getLong(AbstractByteBuf wrapped, int index);

  protected abstract void _setShort(AbstractByteBuf wrapped, int index, short value);

  protected abstract void _setInt(AbstractByteBuf wrapped, int index, int value);

  protected abstract void _setLong(AbstractByteBuf wrapped, int index, long value);
}
